class BlackDeathZombiePawn extends BlackDeathPawn
	placeable;
var name lastStateBeforeStagger;

var bool bAfraid;//Weither the pawn is currently "afraid" and moveing away.   be very afraid
var bool bIncapped;//True of pawn incapacitated
var bool bRevive;

var int ReviveHP;//Health to have upon reviveing from death
var int ReviveTime;//time before a zombie revives

var bool bPlayingdamage;//Ture if the pawn is current playing a damage anim

var actor FearObject; //The source of a zombie's fear

var AnimNodeSequence ZombieSlash;
var AnimNodeSequence DeathNode;
var AnimNodeSequence ReviveNode;
var AnimNodeBlendList PainTypes;
var AnimNodeSequence Notice;

//sounds
var SoundCue Idle_Sounds;


/** count of failed unfeign attempts - kill pawn if too many */
var int UnfeignFailedCount;

simulated function PostBeginPlay()
{
	Super.PostBeginPlay();
	if (!bDeleteMe)
	{
		if (Mesh != None)
		{
			BaseTranslationOffset = Mesh.Translation.Z;
		}
	}
}
	
simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
{
	local AnimNode Node;
	//`log("postinit anim tree");
	super.PostInitAnimTree(SkelComp);
	if (SkelComp == Mesh)
	{
		ZombieSlash = AnimNodeSequence(Mesh.FindAnimNode('ZombieSlash'));
		DeathNode = AnimNodeSequence(Mesh.FindAnimNode('death'));
		ReviveNode = AnimNodeSequence(Mesh.FindAnimNode('Revive'));
		PainTypes = AnimNodeBlendList(Mesh.FindAnimNode('dmgVars'));
		Node = AnimNodeBlend(Mesh.FindAnimNode('Idlevars'));
		Notice = AnimNodeSequence(Mesh.FindAnimNode('Notice'));
		if(node != none)
		{
			//`log("setting idle random");
			AnimNodeBlend(node).SetBlendTarget(FRand(), 0.0);
		}
		Node = AnimNodeBlendList(Mesh.FindAnimNode('WalkVars'));
		if(node != none)
		{
			//`log("setting walk random");
			if(frand() >0.5)
				AnimNodeBlendList(node).SetActiveChild(0.0, 0.0);
			else
				AnimNodeBlendList(node).SetActiveChild(1.0, 0.0);
		}
	}
}


simulated function BeginPursuit()
{
	if(BlackDeathEnemyController(Controller) != none)
		{
			BlackDeathEnemyController(Controller).gotoState('pursuing');
		}
}


simulated function Ignite()
{
	if(!bOnFire)
	{
		super.Ignite();
		if(BlackDeathEnemyController(Controller) != none)
		{
			BlackDeathEnemyController(Controller).gotoState('burning');
		}
	}
}

simulated function PlayWeaponSwing()
{
	changeState(4, 0.1);
}

simulated function EndSwing()
{
	local BlackDeathWeapon BDW;
	`log(Self@": Playing end swing");
	changeState(0, 0.1);
	
	BDW = BlackDeathWeapon(weapon);
	if(BDW != none)
	{
		BDW.EndSwing();
	}
}

simulated event RetreatFromFear(Actor Source)
{
	if(!bonFire)
	{
	FearObject = Source;
	Controller.goToState('Afraid');
	}
}

event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
{
	local int actualDamage;
	local int idx;
	local SeqEvent_TakeDamage dmgEvent;

	if ( (Role < ROLE_Authority) || (Health <= 0) )
	{
		return;
	}

	if ( damagetype == None )
	{
		if ( InstigatedBy == None )
			`warn("No damagetype for damage with no instigator");
		else
			`warn("No damagetype for damage by "$instigatedby.pawn$" with weapon "$InstigatedBy.Pawn.Weapon);
		//scripttrace();
		DamageType = class'DamageType';
	}
	Damage = Max(Damage, 0);

	if (Physics == PHYS_None && DrivenVehicle == None)
	{
		SetMovementPhysics();
	}
	if (Physics == PHYS_Walking && damageType.default.bExtraMomentumZ)
	{
		momentum.Z = FMax(momentum.Z, 0.4 * VSize(momentum));
	}
	momentum = momentum/Mass;

	if ( DrivenVehicle != None )
	{
		DrivenVehicle.AdjustDriverDamage( Damage, InstigatedBy, HitLocation, Momentum, DamageType );
	}

	ActualDamage = Damage;
	WorldInfo.Game.ReduceDamage(ActualDamage, self, instigatedBy, HitLocation, Momentum, DamageType, DamageCauser);
	AdjustDamage(ActualDamage, Momentum, instigatedBy, HitLocation, DamageType, HitInfo, DamageCauser);

	// call Actor's version to handle any SeqEvent_TakeDamage for scripting
		//`log("Zombie Health is:"$Health);
	for (idx = 0; idx < GeneratedEvents.Length; idx++)
	{
		dmgEvent = SeqEvent_TakeDamage(GeneratedEvents[idx]);
		if (dmgEvent != None)
		{
			// notify the event of the damage received
			dmgEvent.HandleDamage(self, InstigatedBy, DamageType, ActualDamage);
		}
	}
	
	Health -=actualDamage;
	if (HitLocation == vect(0,0,0))
	{
		HitLocation = Location;
	}

	if (Health <= 0)
	{
		Health = 1;
		goToState('Incapped');
		Controller.goToState('Incapped');
	}
	else
	{
		PlayHit(actualDamage,InstigatedBy, hitLocation, damageType, Momentum, HitInfo);
		HandleMomentum( momentum, HitLocation, DamageType, HitInfo );
		NotifyTakeHit(InstigatedBy, HitLocation, ActualDamage, DamageType, Momentum);
		if (DrivenVehicle != None)
		{
			DrivenVehicle.NotifyDriverTakeHit(InstigatedBy, HitLocation, actualDamage, DamageType, Momentum);
		}
		if ( instigatedBy != None && instigatedBy != controller )
		{
			LastHitBy = instigatedBy;
		}
		
	}
	MakeNoise(1.0);
}

function PlayHit(float Damage, Controller InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum, TraceHitInfo HitInfo)
{
	Super.PlayHit(Damage, InstigatedBy, HitLocation, damageType, Momentum, HitInfo);
	goToState('PlayingDamage');
}

function int getDamageTypeIndex(class<DamageType> damageType)
{
	switch(damageType)
	{
		case class'DmgType_Crushed':
			return 1;
			break;
		//case class'':
	}
}

function endDamageAnim()
{
}

state PlayingDamage
{
	event BeginState(Name PreviousStateName)
	{
		bPlayingdamage = true;
		changeState(2, 0.2);
		PainTypes.setActivechild(0, 0.2);
		//StateNode.PlayAnim(true,1.0,0.0);
		Controller.goToState('damaged');
	}
	event EndState(Name NextStateName)
	{
		bPlayingdamage = false;
	}
	function endDamageAnim()
	{
		`log("End damage anim called");
		goToState('');
	}
	simulated event RetreatFromFear(Actor Source)
	{
		//dont run away while playing damage
	}
}

function PlayIncapped()
{
	//local Vector ShotDir, ApplyImpulse;
	//StartFallImpactTime = WorldInfo.TimeSeconds;
	//bCanPlayFallingImpacts=true;
	//GotoState('FeigningDeath');

	// if we had some other rigid body thing going on, cancel it

  local vector ApplyImpulse, ShotDir;
  local class<DamageType> DamageType;
  local vector HitLoc;
  	`log(CollisionComponent);
  HitLoc = location;
  DamageType = none;
  Extinguish();
  
  bReplicateMovement = false;
  bTearOff = true;
  Velocity += TearOffMomentum;
  SetDyingPhysics();
  bPlayedDeath = true;
  HitDamageType = DamageType; // these are replicated to other clients
  TakeHitLocation = HitLoc;
  //if ( WorldInfo.NetMode == NM_DedicatedServer )
  //{
   // GotoState('Dying');
  //  return;
  //}


      InitRagdoll();
	      mesh.MinDistFactorForKinematicUpdate = 0.f;
  if (Physics == PHYS_RigidBody)
  {
    //@note: Falling instead of None so Velocity/Acceleration don't get cleared
    setPhysics(PHYS_Falling);
  }
  	PreRagdollCollisionComponent = CollisionComponent;
  //CollisionComponent = Mesh;
    SetPawnRBChannels(TRUE);
  Mesh.ForceSkelUpdate();
  	bBlendOutTakeHitPhysics = false;
    if( Mesh.bNotUpdatingKinematicDueToDistance )
  {
    Mesh.ForceSkelUpdate();
    Mesh.UpdateRBBonesFromSpaceBases(TRUE, TRUE);
  }
    CylinderComponent.SetActorCollision(false, false);
	Mesh.PhysicsAssetInstance.SetAllBodiesFixed(FALSE);
    //Mesh.SetActorCollision(true, true);
	Mesh.SetTraceBlocking(true, true);
	SetPhysics(PHYS_RigidBody);
	Mesh.PhysicsWeight = 1.0;

	SetPawnRBChannels(true);
	Mesh.bUpdateKinematicBonesFromAnimation=false;
	  Mesh.SetRBLinearVelocity(Velocity, false);
  mesh.SetTranslation(vect(0,0,1) * BaseTranslationOffset);
    Mesh.ScriptRigidBodyCollisionThreshold = MaxFallSpeed;
  Mesh.SetNotifyRigidBodyCollision(true);
  Mesh.WakeRigidBody();
  if( TearOffMomentum != vect(0,0,0) )
  {
    ShotDir = normal(TearOffMomentum);
    ApplyImpulse = ShotDir * DamageType.default.KDamageImpulse;
    // If not moving downwards - give extra upward kick
    if ( Velocity.Z > -10 )
    {
      ApplyImpulse += Vect(0,0,1)*2;
    }
    Mesh.AddImpulse(ApplyImpulse, TakeHitLocation,, true);
  }
}

function playRevive()
{
	local vector FeignLocation, HitLocation, HitNormal, TraceEnd, Impulse;
	local rotator NewRotation;
	//local BlackDeathWeapon BDWeap;
	local float UnFeignZAdjust;
	
	// fit cylinder collision into location, crouching if necessary
	FeignLocation = Location;
	CollisionComponent = PreRagdollCollisionComponent;
	TraceEnd = Location + vect(0,0,1) * GetCollisionHeight();
	if (Trace(HitLocation, HitNormal, TraceEnd, Location, true, GetCollisionExtent()) == None )
	{
		HitLocation = TraceEnd;
	}
	if ( !SetFeignEndLocation(HitLocation, FeignLocation) )
	{
		//`log("finding an unfeign location");
		//CollisionComponent = Mesh;
		SetLocation(FeignLocation);
		Impulse = VRand();
		Impulse.Z = 0.5;
		Mesh.AddImpulse(800.0*Impulse, Location);
		UnfeignFailedCount++;
		if ( UnFeignfailedCount > 4 )
		{
			Suicide();
		}
		return;
	}

	// Calculate how far we just moved the actor up.
	UnFeignZAdjust = Location.Z - FeignLocation.Z;
	// If its positive, move back down by that amount until it hits the floor
	if(UnFeignZAdjust > 0.0)
	{
		moveSmooth(vect(0,0,-1) * UnFeignZAdjust);
	}


	UnfeignFailedCount = 0;

	//CollisionComponent = Mesh;
	//bPlayingFeignDeathRecovery = true;
	//FeignDeathRecoveryStartTime = WorldInfo.TimeSeconds;

	// don't need collision events anymore
	Mesh.SetNotifyRigidBodyCollision(false);
	SetPhysics(PHYS_falling);

	
		// physics weight interpolated to 0 in C++, then StartFeignDeathRecoveryAnim() is called
		Mesh.PhysicsWeight = 1.0;
		//FeignDeathBlend.SetBlendTarget(1.0, 0.0);
		// force rotation to match the body's direction so the blend to the getup animation looks more natural
		NewRotation = Rotation;
		NewRotation.Yaw = rotator(Mesh.GetBoneAxis('VM__spine_low_jnt', AXIS_X)).Yaw;
		// flip it around if the head is facing upwards, since the animation for that makes the character
		// end up facing in the opposite direction that its body is pointing on the ground
		// FIXME: generalize this somehow (stick it in the AnimNode, I guess...)
		/*if (Mesh.GetBoneAxis('VM__head_a_jnt', AXIS_Y).Z < 0.0)
		{
			NewRotation.Yaw += 32768;
		}*/
		SetRotation(NewRotation);
		
	RestorePreRagdollCollisionComponent();
	Mesh.PhysicsWeight = 0.f;
	Mesh.MinDistFactorForKinematicUpdate = default.Mesh.MinDistFactorForKinematicUpdate;
	Mesh.PhysicsAssetInstance.SetAllBodiesFixed(TRUE);
	Mesh.PhysicsAssetInstance.SetFullAnimWeightBonesFixed(FALSE, Mesh);
	SetPawnRBChannels(FALSE);
	Mesh.bUpdateKinematicBonesFromAnimation=TRUE;

	// Turn collision on for cylinder and off for skelmeshcomp
	CylinderComponent.SetActorCollision(true, true);
	//Mesh.SetActorCollision(false, false);
	Mesh.SetTraceBlocking(false, false);
	`log(CollisionComponent);
		if (Physics == PHYS_RigidBody)
		{
			setPhysics(PHYS_Falling);
		}

}

simulated function bool SetFeignEndLocation(vector HitLocation, vector FeignLocation)
{
	local vector NewDest;

	if ( SetLocation(HitLocation) && CheckValidLocation(FeignLocation) )
	{
		return true;
	}

	// try crouching
	ForceCrouch();
	if ( SetLocation(HitLocation) && CheckValidLocation(FeignLocation) )
	{
		return true;
	}

	newdest = HitLocation + GetCollisionRadius() * vect(1,1,0);
	if ( SetLocation(newdest) && CheckValidLocation(FeignLocation) )
		return true;
	newdest = HitLocation + GetCollisionRadius() * vect(1,-1,0);
	if ( SetLocation(newdest) && CheckValidLocation(FeignLocation) )
		return true;
	newdest = HitLocation + GetCollisionRadius() * vect(-1,1,0);
	if ( SetLocation(newdest) && CheckValidLocation(FeignLocation) )
		return true;
	newdest = HitLocation + GetCollisionRadius() * vect(-1,-1,0);
	if ( SetLocation(newdest) && CheckValidLocation(FeignLocation) )
		return true;

	return false;
}

simulated function bool CheckValidLocation(vector FeignLocation)
{
	local vector HitLocation, HitNormal, DestFinalZ;

	// try trace down to dest
	if (Trace(HitLocation, HitNormal, Location, FeignLocation, false, vect(10,10,10),, TRACEFLAG_Bullet) == None)
	{
		return true;
	}

	// try trace straight up, then sideways to final location
	DestFinalZ = FeignLocation;
	FeignLocation.Z = Location.Z;
	if ( Trace(HitLocation, HitNormal, DestFinalZ, FeignLocation, false, vect(10,10,10)) == None &&
		Trace(HitLocation, HitNormal, Location, DestFinalZ, false, vect(10,10,10),, TRACEFLAG_Bullet) == None )
	{
		return true;
	}
	return false;
}

simulated function SetPawnRBChannels(bool bRagdollMode)
{
	if(bRagdollMode)
	{
		Mesh.SetRBChannel(RBCC_Pawn);
		Mesh.SetRBCollidesWithChannel(RBCC_Default,TRUE);
		Mesh.SetRBCollidesWithChannel(RBCC_Pawn,TRUE);
		Mesh.SetRBCollidesWithChannel(RBCC_Vehicle,TRUE);
		Mesh.SetRBCollidesWithChannel(RBCC_Untitled3,FALSE);
		Mesh.SetRBCollidesWithChannel(RBCC_BlockingVolume,TRUE);
	}
	else
	{
		Mesh.SetRBChannel(RBCC_Untitled3);
		Mesh.SetRBCollidesWithChannel(RBCC_Default,FALSE);
		Mesh.SetRBCollidesWithChannel(RBCC_Pawn,FALSE);
		Mesh.SetRBCollidesWithChannel(RBCC_Vehicle,FALSE);
		Mesh.SetRBCollidesWithChannel(RBCC_Untitled3,TRUE);
		Mesh.SetRBCollidesWithChannel(RBCC_BlockingVolume,FALSE);
	}
}

function finishRevive()
{
	bRevive = false;
	changeState(0, 0.0);
	goToState('');
}

state Incapped
{
	event BeginState(Name PreviousStateName)
	{
		SetTimer(ReviveTime, false, 'Revive');
		Extinguish();
		//`log(DeathNode);
		changeState(3, 0.0);
		DeathNode.PlayAnim(false, 1.0, 0.0);
		SetTimer(0.7, false, 'PlayIncapped');
		bIncapped = true;
		bPlayingdamage = false;
		Controller.goToState('Incapped');
		//PlayIncapped();
	}
	
	function Revive()
	{
		Health = ReviveHP;
		playRevive();
		//`log("Begin undead anim");
		bRevive = true;
		bIncapped = false;
		ReviveNode.PlayAnim(false, 1.0, 0.0);
		//SetTimer(0.75, false, 'finishRevive');
	}
	
	event EndState(Name NextStateName)
	{
		//`log("finsh undead anim");
	}
	
	simulated function Ignite()
	{
	}
	
	simulated event RetreatFromFear(Actor Source)
	{
		//dont run away if your down for the count
	}
	
	event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
	{
		local Controller Killer;
		if(class'DmgType_Finish'==DamageType)
		{
			PlayHit(1,InstigatedBy, hitLocation, damageType, Momentum, HitInfo);
			MakeNoise(1.0);

			if ( (Role < ROLE_Authority) || (Health <= 0) )
			{
				return;
			}

			if (Physics == PHYS_None && DrivenVehicle == None)
			{
				SetMovementPhysics();
			}
			if (Physics == PHYS_Walking && damageType.default.bExtraMomentumZ)
			{
				momentum.Z = FMax(momentum.Z, 0.4 * VSize(momentum));
			}

			Health -= 1;
			if (HitLocation == vect(0,0,0))
			{
				HitLocation = Location;
			}
			Killer = SetKillInstigator(InstigatedBy, DamageType);
			TearOffMomentum = momentum;
			Died(Killer, damageType, HitLocation);
			
		}
	}
}

simulated function StaggerRecover()
{
}
State Staggered
{
	event BeginState(Name PreviousStateName)
	{
		lastStateBeforeStagger = PreviousStateName;
		AirSpeed=20.000000;
		GroundSpeed=20.000000;
		setTimer(0.5, false, 'StaggerRecover');
		Controller.gotoState('Staggered');
	}
	
	simulated event RetreatFromFear(Actor Source)
	{
		//dont run away if your staggering
	}
	
	simulated function StaggerRecover()
	{
		goToState(lastStateBeforeStagger);
	}
	
	event EndState(Name NextStateName)
	{
		AirSpeed=75.000000;
		GroundSpeed=60.000000;
		clearTimer('StaggerRecover');
	}
}

DefaultProperties
{
	InventoryManagerClass=class'BlackDeathInventoryManager'
	InitialInventory(0)=class'BD_Claws'
//Setting up the light environment
    Begin Object Name=MyLightEnvironment
        ModShadowFadeoutTime=0.25
        MinTimeBetweenFullUpdates=0.2
        AmbientGlow=(R=.01,G=.01,B=.01,A=1)
        AmbientShadowColor=(R=0.15,G=0.15,B=0.15)
        bSynthesizeSHLight=TRUE
    End Object
    Components.Add(MyLightEnvironment)
	LightEnvironment=MyLightEnvironment
//Setting up the mesh and animset components
    Begin Object Name=InitialSkeletalMesh
        CastShadow=true
        bCastDynamicShadow=true
        bOwnerNoSee=false
        LightEnvironment=MyLightEnvironment;
		BlockRigidBody=true;
		CollideActors=true;
		BlockZeroExtent=true;
//What to change if you'd like to use your own meshes and animations
        PhysicsAsset=PhysicsAsset'BD_CHARACTERS.Mesh.Zombie_Physics'
        AnimSets(0)=AnimSet'BD_CHARACTERS.Animation.ZombieMoveSet'
        AnimTreeTemplate=AnimTree'BD_CHARACTERS.Animation.Zombie_AnimTree'
        SkeletalMesh=SkeletalMesh'BD_CHARACTERS.Mesh.Zombie'
    End Object
//Setting up a proper collision cylinder
    Mesh=InitialSkeletalMesh;
    Components.Add(InitialSkeletalMesh); 
	CollisionType=COLLIDE_BlockAll
	Begin Object Name=CollisionCylinder
		CollisionRadius=+0024.000000
		CollisionHeight=+0044.000000
	End Object
	CylinderComponent=CollisionCylinder
	
	BaseTranslationOffset=6.0

	HealthMax = 100
	
	bIncapped = false
	bRevive =false
	bPlayingdamage = false
	
	ReviveHP = 30
	ReviveTime = 25.0
	
	AirSpeed=75.000000
	GroundSpeed=60.000000
	bCanBeDamaged=true
	bCanSwim=false
}